//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler
// FILE:            xfile.h - native file io support
// AUTHOR:          Vladimir Gumenuk
// DATE:            21-May-2013
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#pragma once

// Contains mixed implementation of File class C/XORT
#ifdef CPP_COMPILER
    #define BUFPTR(x) x.ptr(0)
    #ifdef ARCH_64BIT
        #define DWORD_CONV (dword)
    #else
        #define DWORD_CONV
        
    #endif
#else //CPP_COMPILER
    #define BUFPTR(x) x
    #define DWORD_CONV

// native imports declaration

// Here is the big native imports problem: the size of the handle for win32 and win64? :)
#ifdef ARCH_64BIT
    typedef qword handle;
#else
    typedef dword handle;
#endif

[import("proc") name("fopen") platform("windows") module("msvcrt.dll") callconv("cdecl")]
handle fopen( string Filename, string Mode );

[import("proc") name("fclose") platform("windows") module("msvcrt.dll") callconv("cdecl")] 
int fclose( handle hFile );

[import("proc") name("fwrite") platform("windows") module("msvcrt.dll") callconv("cdecl")] 
dword fwrite( BYTEARRAY Str, dword Size, dword Count, handle hFile );

[import("proc") name("fread") platform("windows") module("msvcrt.dll") callconv("cdecl")] 
dword fread( BYTEARRAY DstBuf, dword ElementSize, dword Count, handle hFile );

[import("proc") name("fprintf") platform("windows") module("msvcrt.dll") callconv("cdecl")] 
int fprintf( handle hFile, string csFormat, ... );

[import("proc") name("fseek") platform("windows") module("msvcrt.dll") callconv("cdecl")] 
int fseek( handle hFile, int lOffset, int iOrigin );

[import("proc") name("ftell") platform("windows") module("msvcrt.dll") callconv("cdecl")] 
dword ftell( handle hFile );
/*
[import("proc") name("fopen") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
handle fopen( string Filename, string Mode );

[import("proc") name("fclose") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
int fclose(handle hFile);

[import("proc") name("fwrite") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
dword fwrite(BYTEARRAY Str, dword Size, dword Count, handle hFile);

[import("proc") name("fread") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
dword fread(BYTEARRAY DstBuf, dword ElementSize, dword Count, handle hFile);

[import("proc") name("fprintf") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
int fprintf(handle hFile, string csFormat, ...);

[import("proc") name("fseek") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
int fseek(handle hFile, int lOffset, int iOrigin);

[import("proc") name("ftell") platform("osx") module("libsystem_c.dylib") callconv("cdecl")]
dword ftell(handle hFile);
*/
#endif // #ifndef CPP_COMPILER

// See kPosition 
const dword Seek_Begin = 0;
const dword Seek_Current = 1;
const dword Seek_End = 2;
const dword Seek_Set =  0;

struct File
{
    string   m_strFileName;
#ifdef CPP_COMPILER
    FILE    *m_hFile;
    File() { m_hFile = NULL; }
#else
    handle   m_hFile;
#endif

    int Open( string csFileName, string csMode )
    {
        if( m_hFile )
            return FALSE;

        m_strFileName.Assign( csFileName );

#ifdef CPP_COMPILER
        m_hFile = fopen( m_strFileName.GetBuffer(), csMode.GetBuffer() );
#else
        m_hFile = fopen( m_strFileName, csMode );
#endif
        if( !m_hFile )
        {
            m_strFileName.Empty();
            return FALSE;
        }

        return TRUE;
    }

    int isOpened()
    {
        return m_hFile != 0 ? TRUE : FALSE;
    }

    string Name() { return m_strFileName; }
    
    dword Length()
    {
        if( !m_hFile )
            return 0;

        dword dwCurPos = ftell( m_hFile );
        fseek( m_hFile, 0, Seek_End );
        dword dwLength = ftell( m_hFile );
        fseek( m_hFile, dwCurPos, Seek_Set );
        return dwLength;
    }

    dword Read( BYTEARRAY buf, dword dwCount )
    {
        if( !m_hFile )
            return 0;

        if( buf.count() != dwCount )
        {
            buf.zero();
            buf.reserve( dwCount );
        }

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, dwCount, m_hFile );
        buf.set_itemsCount( dwRead );

        return dwRead;
    }

    dword WriteByteArray( BYTEARRAY buf )
    {
        if( !m_hFile )
            return 0;

        dword dwCount = buf.count();
        if( dwCount == 0 )
            return 0;

        dword dwWritten = DWORD_CONV fwrite( BUFPTR(buf), 1, dwCount, m_hFile );
        return dwWritten;
    }

    dword WriteDwordArray( DWORDARRAY buf )
    {
        if( !m_hFile )
            return 0;

        dword dwCount = buf.count() * 4;
        if( dwCount == 0 )
            return 0;

        dword dwWritten = DWORD_CONV fwrite( BUFPTR(buf), 1, dwCount, m_hFile );
        return dwWritten;
    }

    dword WriteByte( byte bVal )
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.storeByte( bVal ); // yeah, this is the only way for now

        return WriteByteArray( buf );
    }

    dword WriteWord( word wVal )
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.storeWord( wVal ); // yeah, this is the only way for now

        return WriteByteArray( buf );
    }

    dword WriteDword( dword dwVal )
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.storeDword( dwVal );

        return WriteByteArray( buf );
    }

    dword WriteInt( int iVal )
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.storeInt( iVal );

        return WriteByteArray( buf );
    }

    dword WriteQword( qword qwVal )
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.storeQword( qwVal );

        return WriteByteArray( buf );
    }

    dword WriteString( string csStr )
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        dword i;
        dword dwLen = csStr.Length();

        for( i = 0; i < dwLen; ++i )
        {
            buf.storeByte( csStr.CharAt( i ) );
        }

        return WriteByteArray( buf );
    }

    string ReadString( dword dwLength )
    {
        if( !m_hFile )
#ifdef CPP_COMPILER
            return "";
#else
            return NULL;
#endif

        BYTE_ARRAY buf;
        buf.reserve( dwLength );

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, dwLength, m_hFile );
        if( dwRead != dwLength )
#ifdef CPP_COMPILER
            return "";
#else
            return NULL;
#endif

        buf.set_itemsCount( dwRead );

        string csRet;
        dword i;
        for( i = 0; i < dwLength; ++i )
        {
            csRet.Append( buf.get( i ) );
        }

        return csRet;
    }

    byte ReadByte()
    {
        if( !m_hFile )
            return 0;
        
        BYTE_ARRAY buf;
        buf.reserve( 1 );

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, 1, m_hFile );
        if( dwRead != 1 )
            return 0;

        buf.set_itemsCount( dwRead );

        return buf.get( 0 );
    }

    word ReadWord()
    {
        if( !m_hFile )
            return 0;
        
        BYTE_ARRAY buf;
        buf.reserve( 2 );

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, 2, m_hFile );
        if( dwRead != 2 )
            return 0;

        buf.set_itemsCount( dwRead );

        return buf.getWord( 0 );
    }

    dword ReadDword()
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.reserve( 4 );

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, 4, m_hFile );
        if( dwRead != 4 )
            return 0;

        buf.set_itemsCount( dwRead );

        return buf.getDword( 0 );
    }

    dword ReadInt()
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.reserve( 4 );

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, 4, m_hFile );
        if( dwRead != 4 )
            return 0;

        buf.set_itemsCount( dwRead );

        return buf.getInt( 0 );
    }

    qword ReadQword()
    {
        if( !m_hFile )
            return 0;

        BYTE_ARRAY buf;
        buf.reserve( 8 );

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, 8, m_hFile );
        if( dwRead != 8 )
            return 0;

        buf.set_itemsCount( dwRead );

        return buf.getQword( 0 );
    }

    dword ReadDwordArray( DWORDARRAY buf, dword dwCount )
    {
        if( !m_hFile )
            return 0;

        if( buf.count() < dwCount )
        {
            buf.reserve( dwCount );
        }

        dword dwRead = DWORD_CONV fread( BUFPTR(buf), 1, dwCount * 4, m_hFile );
        if( dwRead != dwCount * 4 )
            return 0;

#ifdef CPP_COMPILER
        buf.set_itemsCount( dwRead );
#endif
        return dwRead;
    }

    void Close()
    {
        if( m_hFile )
        {
            fclose( m_hFile );
            m_hFile = 0;

            m_strFileName.Empty();
        }
    }
}EOFSTRUCT
